﻿
package extremefx.tools {

	/**
	 * @author Marcelo Volmaro
	 */
	public final class DateUtils {
		private static var _timeObj:Object = {
			ms: 1,
			ss: 1000,
			mn: 60000,
			hh: 3600000,
			dd: 86400000,
			wk: 604800000
		};
		
		/**
		 * Adds some ammount of "units" to a Date object.
		 * @param pDate The date to add the units
		 * @param pAmmount The ammount of units to be added.
		 * @param pUnit The units to add: [ms:milliseconds | ss:seconds | mn:minutes | hh:hours | dd:days | wk:weeks | mm:months | yy:years]
		 * @return The new date.
		 */
		public static function add(pDate:Date, pAmmount:int, pUnit:String):Date {
			var d:Date = new Date(pDate);
	
			if (pUnit == "mm") {
				d.setMonth(pDate.getMonth() + pAmmount);
	
			} else if (pUnit == "yy") {
				d.setFullYear(pDate.getFullYear() + pAmmount);
	
			} else {
				d.setTime(pDate.getTime() + (_timeObj[pUnit] * pAmmount));
			}
	
			return d;
		}
		
		
		/**
		 * Converts from UTC time to local time.
		 * @param pNow an UTC date
		 * @return a new DateTime object
		 */
		public static function toLocalTime(pNow:Date):Date {
			var dst:Dt = _getDaylightSavingTime(pNow);
			if (dst._dsp){
				return add(pNow, -dst._daylight.getTimezoneOffset(), "mn");
			} 
			
			return add(pNow, -dst._standard.getTimezoneOffset(), "mn");

		}

		/**
		 * Converts from local time to UTC time.
		 */
		public static function toUniversalTime(pNow:Date):Date {
			var dst:Dt = _getDaylightSavingTime(pNow);
			if (dst._dsp){
				return add(pNow, dst._daylight.getTimezoneOffset(), "mn");
			} 
			
			return add(pNow, dst._standard.getTimezoneOffset(), "mn");
		}
		
		public static function isDaylightSavingTime(pDate:Date):Boolean {
			return _getDaylightSavingTime(pDate)._dsp;
		}

		private static function _getDaylightSavingTime(pDate:Date):Dt {
			var intTimezone_now:Number = pDate.getTimezoneOffset();
			var intFullYear_now:Number = pDate.getFullYear();
			
			var objDate_month1:Date	 = new Date("1 Jan " + intFullYear_now + " 00:00:00 " + _getTimezoneString(pDate));
			var objDate_month7:Date = new Date("1 Jul " + intFullYear_now + " 00:00:00 " + _getTimezoneString(pDate));
			var objDate_month12:Date = new Date("1 Dec " + intFullYear_now + " 00:00:00 " + _getTimezoneString(pDate));
			
			var intTimezone_month1:Number = objDate_month1.getTimezoneOffset();
			var intTimezone_month7:Number = objDate_month7.getTimezoneOffset();
			var blnDaylightSaving:Boolean = false;
			
			if (intTimezone_month1 != intTimezone_month7) {
				var intNSHemisphere:int = intTimezone_month1 > intTimezone_month7 ? 1 : 2;
		
				// Calculate boths dates at which clocks change
				var objDaylightStart:Date = intNSHemisphere == 1 ? _getDaylightSavingDay(objDate_month1, objDate_month7) : _getDaylightSavingDay(objDate_month7, objDate_month12);
				var objDaylightEnd:Date = intNSHemisphere == 1 ? _getDaylightSavingDay(objDate_month7, objDate_month12) : _getDaylightSavingDay(objDate_month1, objDate_month7);
		
				// Work out then if daylight saving is currently in force
				if (objDaylightStart.getTimezoneOffset() == intTimezone_now) {
					blnDaylightSaving = true;
				}
				
				// If start-date is after end date then swap displayed results around
				if (objDaylightStart > objDaylightEnd) {
					var temp:Date = objDaylightEnd;
					objDaylightEnd = objDaylightStart;
					objDaylightStart = temp;
				}
			}
			
			return new Dt(objDaylightStart, objDaylightEnd, blnDaylightSaving);
		}
		
		private static function _getDaylightSavingDay(objIterationMin:Date, objIterationMax:Date):Date {
			var objTestDateOld:Date	= new Date(objIterationMin);
			var objTestDate:Date = new Date(objIterationMax);
		
			while(Math.abs(objTestDate.getTime() - objTestDateOld.getTime()) > 0) {
				objTestDateOld = objTestDate;
				objTestDate = new Date(objIterationMin.getTime() + Math.round((objIterationMax.getTime() - objIterationMin.getTime()) / 2));

				if (objTestDate.getTimezoneOffset() == objIterationMin.getTimezoneOffset()) {
					objIterationMin = objTestDate;
					
				} else {
					objIterationMax = objTestDate;
				}
			}
			
			return objTestDate;
		}
		
		private static function _getTimezoneString(pDate:Date):String {
			var intDateTZ:Number = pDate.getTimezoneOffset();
			var intDateTZ_hours:int = Math.floor(Math.abs(intDateTZ) / 60);
			var intDateTZ_minutes:int = Math.abs(intDateTZ_hours - (Math.abs(intDateTZ) / 60)) * 60;
			
			return "UTC" + 
			(intDateTZ > 0 ? "-" : "+") + 
			StringUtils.padLeft(intDateTZ_hours.toString(), 2, "0") +
			StringUtils.padLeft(intDateTZ_minutes.toString(), 2, "0"); 
		}
	}
}


final class Dt {
	public var _daylight:Date;
	public var _standard:Date;
	public var _dsp:Boolean;

	public function Dt(pStandard:Date, pDaylight:Date, pDST:Boolean) {
		_standard = pStandard;
		_daylight = pDaylight;
		_dsp = pDST;
	}
}